package com.hooya.fa.eu.biz.service.impl;

import com.alibaba.excel.EasyExcel;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.hooya.fa.eu.api.bo.*;
import com.hooya.fa.eu.api.bo.report.ProfitCheckBO;
import com.hooya.fa.eu.api.enums.PkTypeEnum;
import com.hooya.fa.eu.api.util.FileUtil;
import com.hooya.fa.eu.api.vo.ProfitInfoVO;
import com.hooya.fa.eu.biz.dao.entity.*;
import com.hooya.fa.eu.biz.dao.manager.special.ProfitCheckInitManager;
import com.hooya.fa.eu.biz.dao.mapper.*;
import com.hooya.fa.eu.biz.dao.mapper.stk.TempJcjlMapper;
import com.hooya.fa.eu.biz.dao.mapper.stk.TempThjlMapper;
import com.hooya.fa.eu.biz.dao.mapper.stk.TempZyjlMapper;
import com.hooya.fa.eu.biz.dao.repository.ProfitCheckDailyRepository;
import com.hooya.fa.eu.biz.dao.repository.ProfitCheckRecordRepository;
import com.hooya.fa.eu.biz.dao.repository.ProfitCheckRepository;
import com.hooya.fa.eu.biz.service.DictRelationService;
import com.hooya.fa.eu.biz.service.ProfitService;
import com.hooya.integrated.common.core.exception.CommonException;
import com.hooya.integrated.common.core.util.BeanCopyUtils;
import com.hooya.integrated.common.other.data.DataMethod;
import com.hooya.integrated.common.util.currency.CurrencyConvertUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.hooya.fa.eu.api.constant.FileNameConstant.PROFIT_CHECK_FOLDER_NAME;

/**
 * @Description 1
 * @Author : Zongz
 * @Copyright Copyright (c) 豪雅集团
 * @Create 2024/12/10
 * @Version 1.1.0
 */
@Slf4j
@Service
@DS("fa_eu")
@RequiredArgsConstructor
public class ProfitServiceImpl implements ProfitService {

    private static final List<String> THIRD_PLATFORM_LIST =
            Arrays.asList("EU73", "EU78", "EU79", "EU80", "EU81", "EU82", "EU83", "EU84", "EU85", "EU86", "EU89");

    private static final List<String> COSTWAY_COMPANY_LIST =
            Arrays.asList("costway-fr", "costway-it", "costway-pl", "costway-de");

    private final IdentifierGenerator identifierGenerator;

    private final DictRelationService dictRelationService;

    private final ProfitCheckInitManager profitCheckInitManager;

    private final ProfitCheckRepository profitCheckRepository;

    private final ProfitCheckDailyRepository profitCheckDailyRepository;

    private final ProfitCheckRecordRepository profitCheckRecordRepository;

    private final TempKcjlDailyMapper tempKcjlDailyMapper;

    private final TempJcjlMapper tempJcjlMapper;

    private final TempZyjlMapper tempZyjlMapper;

    private final TempThjlMapper tempThjlMapper;

    private final ProfitCheckMapper profitCheckMapper;

    private final TempKcjlDailyHyMapper tempKcjlDailyHyMapper;

    private final DiffKcMapper diffKcMapper;

    private final PkMapper pkMapper;

    private final MetaRelationMapper metaRelationMapper;

    @Override
    @DSTransactional
    public void initProfitByKcjl(String date) {
        List<ProfitCheckBO> res = new LinkedList<>();
        //所有costway库存
        List<TempKcjlDaily> kcList = tempKcjlDailyMapper.getCostwayListByDate(date);
        Map<String, List<TempKcjlDaily>> kcMap = kcList.stream().collect(Collectors.groupingBy(l ->
                l.getCpbh() + "_" + l.getPlace() + "_" + l.getBelongs()));

        //所有转运
        List<PlatformZyInfoBO> allZyList = tempZyjlMapper.getAllCostwayZyList();

        //所有进仓
        List<JcInfoBO> allJcList = tempJcjlMapper.getAllCostwayJcList(date);
        allJcList = allJcList.stream().distinct().collect(Collectors.toList());
        //进仓（含历史）
        List<JcInfoBO> jcInfoList = tempJcjlMapper.getJcInfoByKcInfo(date);
        List<JcInfoBO> hisJcInfoList = tempJcjlMapper.getHisJcInfoByKcInfo(date);
        jcInfoList.addAll(hisJcInfoList);
        jcInfoList = jcInfoList.stream().distinct().collect(Collectors.toList());
        Map<String, List<JcInfoBO>> jcInfoMap = jcInfoList.stream().collect(Collectors.groupingBy(l ->
                l.getSku() + "_" + l.getPlace() + "_" + l.getBelongs()));
        //自营仓【存在三方仓转到自营仓的情况】/三方仓转运(先转正常仓，再转三方仓)
        List<PlatformZyInfoBO> localList = tempKcjlDailyMapper.getPlatformListByDate(date, THIRD_PLATFORM_LIST, null);

        // 补充历史转运(本地仓转本地仓)
        List<PlatformZyInfoBO> hisZyList = tempKcjlDailyMapper.getHisLocalZyList(date, THIRD_PLATFORM_LIST, null);
        localList.addAll(hisZyList);
        Map<String, List<PlatformZyInfoBO>> localZyInfoMap = localList.stream()
                .filter(l -> StringUtils.isNotBlank(l.getHthm()))
                .sorted(Comparator.comparing(PlatformZyInfoBO::getHtrq).reversed())
                .collect(Collectors.groupingBy(l -> l.getSku() + "_" + l.getPlace() + "_" + l.getBelongs()));

        //总体思路，进仓如果直接补齐，直接用；未补齐由转运进行补充（正常仓->正常仓，正常仓->三方，总数量由 jcInfoList 控制）
        List<JcInfoBO> lostList = new LinkedList<>();
        for (TempKcjlDaily kc : kcList) {
            // ------------------------- 进仓处理 -------------------------
            String jcKey = kc.getCpbh() + "_" + kc.getPlace() + "_" + kc.getBelongs();
            List<JcInfoBO> list = jcInfoMap.get(jcKey);
            if (CollectionUtils.isEmpty(list)) {
                if (kc.getBelongs().endsWith("DE")) {
                    List<JcInfoBO> specialJcList = allJcList.stream().filter(l ->
                                    l.getSku().equals(kc.getCpbh()) && l.getPlace().equals(kc.getPlace()))
                            .collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(specialJcList)) {
                        jcInfoList.addAll(specialJcList);
                        list = jcInfoList.stream().filter(l ->
                                        l.getSku().equals(kc.getCpbh()) && l.getPlace().equals(kc.getPlace()))
                                .collect(Collectors.toList());
                    }
                }
            }
            if (CollectionUtils.isEmpty(list)) {
                continue;
            }
            //有的进行remove，后续剩余不是三方仓的直接进lost
            kcMap.remove(jcKey);
            //开始补充数据
            int memoNum = 0;
            int remainingNum = 0;
            for (int i = 0; i < list.size(); i++) {
                JcInfoBO info = list.get(i);
                if (i == 0) {
                    //控制相同key是否存在不一样的数量
                    memoNum = kc.getSl();
                    //还剩多少数量
                    remainingNum = kc.getSl();
                } else {
                    CommonException.throwExOnCondition(info.getNum() != memoNum, "0_0",
                            "相同key数据数量存在冲撞，请补充逻辑，key：" + jcKey);
                }
                Integer cgSl = info.getCgSl();
                //只用于遍历整个list的，处理数量冲突情况
                if (remainingNum == 0) {
                    continue;
                }
                int currentNum = cgSl - remainingNum;
                //相同key的了进完，再进行转运数据
                if (currentNum >= 0) {
                    //扣完了
                    int finalRemainingNum = remainingNum;
                    res.add(profitCheckInitManager.genJcProfitCheckBO(info, kc.getBelongs(), info.getHtrq(), info.getHthm(), finalRemainingNum, date));
                    remainingNum = 0;
                    //更新数量，加入到剩余进仓中去
                    info.setCgSl(currentNum);
                    continue;
                }
                //不够扣
                remainingNum = Math.abs(cgSl - remainingNum);
                res.add(profitCheckInitManager.genJcProfitCheckBO(info, kc.getBelongs(), info.getHtrq(), info.getHthm(), cgSl, date));
                info.setCgSl(0);
                //进仓用完了，记录，从转运补充
                if (i == list.size() - 1 && remainingNum != 0) {
                    //进仓还是不够，进缺失列表
                    int num = remainingNum;
                    lostList.add(BeanCopyUtils.copyPropertiesNew(info, JcInfoBO::new, (s, t) -> t.setNum(num)));
                }
            }

        }

        //所有sku低于date的最新价格
        List<SkuLastPriceBO> priceInfoList = dictRelationService.getSkuLastPriceInfo(date);
        Map<String, SkuLastPriceBO> priceMap = priceInfoList.stream().collect(Collectors.toMap(SkuLastPriceBO::getSku, i -> i));

        //筛选kcMap不是三方仓的进入lostList
        kcMap.forEach((kcKey, data) -> {
//            List<TempKcjlDaily> list = data.stream().filter(l -> !thirdPlatformList.contains(l.getPlace())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(data)) {
                //先做特殊进仓，即批次转DE部分
                lostList.addAll(BeanCopyUtils.copyListProperties(data, JcInfoBO::new, (s, t) -> {
                    t.setSku(s.getCpbh());
                    t.setNum(s.getSl());
                }));
            }
        });

        //lostList数据，从正常仓进行补充，首先从转运自营仓中补充数据
        if (!CollectionUtils.isEmpty(lostList)) {
            for (JcInfoBO lost : lostList) {
                Integer lostNum = lost.getNum();
                String zyKey = lost.getSku() + "_" + lost.getPlace() + "_" + lost.getBelongs();
                List<PlatformZyInfoBO> zyList = localZyInfoMap.get(zyKey);
                //补充转运，只有DE的数据才可能会有
                List<PlatformZyInfoBO> specialZyList;

                if (!CollectionUtils.isEmpty(zyList) && zyList.stream().anyMatch(l -> l.getHtrq().compareTo(date) <= 0)) {
                    zyList = zyList.stream().filter(l -> l.getHtrq().compareTo(date) <= 0).collect(Collectors.toList());
                    //存在变成DE后的转运与DE前的转运
                    if (zyList.stream().mapToInt(PlatformZyInfoBO::getZySl).sum() < lostNum) {
                        if (lost.getBelongs().equals("COSTWAY-DE")) {
                            specialZyList = tempKcjlDailyMapper.getSpecialZyList(date, lost.getSku(), lost.getPlace());
                            if (!CollectionUtils.isEmpty(specialZyList)) {
                                for (PlatformZyInfoBO zy : specialZyList) {
                                    if (zyList.stream().noneMatch(l -> Objects.equals(l.getHthm(), zy.getHthm()))) {
//                                        log.info(zyKey + ",补充转运的发票号有：" + zy.getHthm());
                                        zyList.add(zy);
                                    }
                                }
                            }
                        }
                        if (lost.getPlace().equals("EU09")) {
                            List<PlatformZyInfoBO> EU11To09 = localList.stream().filter(l -> l.getSku().equals(lost.getSku()) &&
                                    StringUtils.isBlank(l.getHthm()) && l.getZcck().equals("EU11") &&
                                    l.getPlace().equals(lost.getPlace())).collect(Collectors.toList());
                            if (!CollectionUtils.isEmpty(EU11To09)) {
                                zyList.addAll(EU11To09);
                            }
                        }
                    }
                } else if (lost.getBelongs().equals("COSTWAY-DE")) {
                    specialZyList = tempKcjlDailyMapper.getSpecialZyList(date, lost.getSku(), lost.getPlace());
                    if (!CollectionUtils.isEmpty(specialZyList)) {
                        zyList = specialZyList;
                    }
                } else {
                    List<PlatformZyInfoBO> EU11To09 = localList.stream().filter(l -> l.getSku().equals(lost.getSku()) &&
                            StringUtils.isBlank(l.getHthm()) && l.getZcck().equals("EU11") &&
                            l.getPlace().equals(lost.getPlace())).collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(EU11To09)) {
                        zyList = EU11To09;
                    }
                }
                //把globalway数据也加进来，数量溢出无所谓
                List<PlatformZyInfoBO> globalway = allZyList.stream().filter(l -> l.getSku().equals(lost.getSku()) &&
                                l.getPlace().equals(lost.getPlace()) && l.getTobelongs().equals("GLOBALWAY"))
                        .collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(zyList)) {
                    if (!CollectionUtils.isEmpty(globalway)) {
                        zyList.addAll(globalway);
                    }
                } else {
                    if (!CollectionUtils.isEmpty(globalway)) {
                        zyList = globalway;
                    } else {
//                        log.info(lost + " 无转运数据");
                        continue;
                    }
                }
                zyList = zyList.stream()
                        .sorted(Comparator.comparing(PlatformZyInfoBO::getHtrq).reversed()).collect(Collectors.toList());
                int enoughZy = zyList.stream().mapToInt(PlatformZyInfoBO::getZySl).sum();
                Integer incrSl = 0;
                for (int i = 0; i < zyList.size(); i++) {
                    PlatformZyInfoBO zy = zyList.get(i);
                    Integer remainingZyNum = zy.getZySl();
                    if (i == 0) {
                        incrSl = lostNum;
                        lostNum = 0;
                    } else if (incrSl == 0) {
                        break;
                    }
                    List<JcInfoBO> filterList = jcInfoList.stream()
                            .filter(l -> l.getHtrq().compareTo(zy.getHtrq()) <= 0)
                            .filter(l -> l.getSku().equals(zy.getSku()) &&
                                    l.getPlace().equals(zy.getZcck()) && l.getBelongs().equals(zy.getFrombelongs()))
                            .collect(Collectors.toList());

                    int jcSl = filterList.stream().mapToInt(JcInfoBO::getCgSl).sum();
                    // 特殊情况（现库存记录已经无sku，place和belongs数据，但转出有，那么就针对找数据【jcInfoList因为联表时间问题所以找不到】）
                    // 有转出但是zySl的量不够库存数量，需要挖掘多次转运的zySl
                    if (CollectionUtils.isEmpty(filterList) || enoughZy - (zy.getZySl() - jcSl) - incrSl < 0 ||
                            (jcSl == 0 && enoughZy >= zy.getZySl())) {
//                        List<JcInfoBO> specialJcList = tempJcjlMapper.getSpecialJcList(date, zy.getSku(), zy.getZcck());
                        List<JcInfoBO> specialJcList = allJcList.stream().filter(l -> l.getSku().equals(zy.getSku()) &&
                                l.getPlace().equals(zy.getZcck())).collect(Collectors.toList());
                        if (!CollectionUtils.isEmpty(specialJcList)) {
                            for (JcInfoBO jc : specialJcList) {
                                if (jcInfoList.stream().noneMatch(l -> l.getHthm().equals(jc.getHthm())
                                        && l.getSku().equals(jc.getSku()) && l.getPlace().equals(jc.getPlace()))) {
                                    jcInfoList.add(jc);
                                }
                            }
                            filterList = jcInfoList.stream()
                                    .filter(l -> l.getHtrq().compareTo(zy.getHtrq()) <= 0)
                                    .filter(l -> l.getSku().equals(zy.getSku()) &&
                                            l.getPlace().equals(zy.getZcck()) &&
                                            (l.getBelongs().equals(zy.getFrombelongs()) || "GLOBALWAY".equals(l.getBelongs())))
                                    .sorted(Comparator.comparing(JcInfoBO::getHtrq).reversed())
                                    .collect(Collectors.toList());
                        }
                        if (CollectionUtils.isEmpty(specialJcList) ||
                                filterList.stream().mapToInt(JcInfoBO::getCgSl).sum() < incrSl) {
                            //转出的sku既不存在库存，又没有进仓，说明该sku也是转运的进仓，源头数据还得深挖
                            //或者数量不够，看看有没有二次转入的可能
                            List<JcInfoBO> fianlJcList = profitCheckInitManager.getFilterList(allZyList, allJcList, jcInfoList,
                                    lost.getSku(), zy.getHtrq(), lost.getHthm(), zy.getZcck(), zy.getFrombelongs());
                            if (!CollectionUtils.isEmpty(fianlJcList)) {
                                filterList.addAll(fianlJcList);
                            } else {
                                if (enoughZy - zy.getZySl() < incrSl) {
                                    //针对有转运无进仓，直接盘盈的情况，但是需要满足【剩下的转运数量不够了】才能进行此操作
                                    SkuLastPriceBO priceInfo = priceMap.get(lost.getSku());
                                    res.add(profitCheckInitManager.genPyProfitCheckBO(lost, priceInfo.getInvoiceDate(), priceInfo.getCurrencyRate(),
                                            priceInfo.getPln(), priceInfo.getInPriceEur(), priceInfo.getInPriceUsd(), zy.getNum()));
                                    lost.setNum(0);
                                    break;
                                }
                            }
                        }
                    }
                    if (!CollectionUtils.isEmpty(filterList)) {
                        for (JcInfoBO jcInfoBO : filterList) {
                            if (jcInfoBO.getCgSl() == 0) {
                                continue;
                            }
                            //存在问题，一张转运如果原始进仓的价格不一致，如何应对？？？
                            //上述问题应对方式（必须找到一致价格，即校验数量达到一致或者价格一致 (问题很多) / 直接用进仓的发票号【现用方式】（存在的问题就是，通过此发票号找不到该sku正确的仓库））
//                            String hthm = StringUtils.isBlank(zy.getHthm()) ? jcInfoBO.getHthm() : zy.getHthm();
                            String hthm = jcInfoBO.getHthm();
                            int a1 = remainingZyNum - incrSl;
                            int a2 = jcInfoBO.getCgSl() - remainingZyNum;
                            int a3 = jcInfoBO.getCgSl() - incrSl;
                            //够扣
                            if (a1 >= 0 && (a2 >= 0 || a3 >= 0)) {
                                /*//存在相同发票号的sku数据,但是价格不一致
                                List<ProfitCheckBO> existInvoiceList = res.stream()
                                        .filter(l -> l.getSku().equals(zy.getSku()) && l.getBelongs().equals(zy.getBelongs()) &&
                                                hthm.equals(l.getInvoiceNumber()) && !jcInfoBO.getPln().equals(l.getPln()))
                                        .collect(Collectors.toList());
                                if (!CollectionUtils.isEmpty(existInvoiceList)) {
                                    //讲原有的新数据删除，
                                    int sum = existInvoiceList.stream().mapToInt(ProfitCheckBO::getRemainingNum).sum();
                                }*/
                                res.add(profitCheckInitManager.genZyProfitCheckBO(jcInfoBO, date, zy.getHtrq(), hthm,
                                        zy.getPlace(), lost.getBelongs(), incrSl));
                                enoughZy -= incrSl;
                                //更新剩余数量（需要检查是否改变了元数据）
                                jcInfoBO.setCgSl(jcInfoBO.getCgSl() - incrSl);
                                incrSl = 0;
                                lost.setNum(0);
                            } else if (a1 < 0 && (a2 < 0 || a3 < 0)) {
                                Integer num;
                                //都不够扣
                                if (a2 >= 0) {
                                    //采购比转运数量多
                                    num = remainingZyNum;
                                } else {
                                    num = jcInfoBO.getCgSl();
                                }
                                res.add(profitCheckInitManager.genZyProfitCheckBO(jcInfoBO, date, zy.getHtrq(), hthm,
                                        zy.getPlace(), lost.getBelongs(), num));
                                enoughZy -= num;
                                remainingZyNum = Math.abs(jcInfoBO.getCgSl() - remainingZyNum);
                                incrSl = Math.abs(num - incrSl);
                                lost.setNum(incrSl);
                                jcInfoBO.setCgSl(jcInfoBO.getCgSl() - num);
                                if (jcInfoBO.getCgSl() > 0) {
                                    //表明这次转运数量用完了
                                    break;
                                }
                            } else {
                                if (a1 >= 0) {
                                    //含有隐式条件【a2 < 0 && a3 < 0】,只扣除采购
                                    res.add(profitCheckInitManager.genZyProfitCheckBO(jcInfoBO, date, zy.getHtrq(), hthm,
                                            zy.getPlace(), lost.getBelongs(), jcInfoBO.getCgSl()));
                                    //todo ??? jcInfoBO.getCgSl()
                                    enoughZy -= zy.getZySl();
                                    remainingZyNum = Math.abs(jcInfoBO.getCgSl() - remainingZyNum);
                                    incrSl = Math.abs(jcInfoBO.getCgSl() - incrSl);
                                    jcInfoBO.setCgSl(0);
                                    lost.setNum(incrSl);
                                } else {
                                    //含有隐式条件【a2 >= 0 && a3 >= 0】,只扣除转运
                                    res.add(profitCheckInitManager.genZyProfitCheckBO(jcInfoBO, date, zy.getHtrq(), hthm,
                                            zy.getPlace(), lost.getBelongs(), remainingZyNum));
                                    enoughZy -= remainingZyNum;
                                    jcInfoBO.setCgSl(jcInfoBO.getCgSl() - remainingZyNum);
                                    incrSl = Math.abs(remainingZyNum - incrSl);
                                    lost.setNum(incrSl);
                                    break;
                                }
                            }
                        }
                    }
                    //直接盘盈，再加上缺失
                    if (i == zyList.size() - 1 && incrSl > 0) {
                        int num = incrSl + lostNum;
                        lost.setNum(num);
//                        log.info(lost + "还缺失数量 " + num + " 件");
                    }
                }
            }
        }

        //退货盘盈
        List<TempThjl> thList = tempThjlMapper.getLtDateList(date);
        //无进仓,无转运(转运问题全面覆盖)
        List<JcInfoBO> remainJcList = lostList.stream().filter(l -> l.getNum() != 0).collect(Collectors.toList());
        for (JcInfoBO jc : remainJcList) {
            List<TempThjl> list = thList.stream().filter(l -> l.getCpbh().equals(jc.getSku()) &&
                    l.getPlace().equals(jc.getPlace()) && l.getBelongs().startsWith("COSTWAY-") &&
                    l.getFlag().equals("addkc")).collect(Collectors.toList());
            SkuLastPriceBO bo = Optional.ofNullable(priceMap.get(jc.getSku())).orElse(new SkuLastPriceBO());
            //有退货就走退货
            if (!CollectionUtils.isEmpty(list)) {
                int sum = list.stream().mapToInt(TempThjl::getSl).sum();
                int i = 0;
                if (sum >= jc.getNum()) {
                    for (TempThjl th : list) {
                        int sl = th.getSl();
                        if (i + sl < jc.getNum()) {
                            res.add(profitCheckInitManager.genPyProfitCheckBO(jc, th.getDate(), bo.getCurrencyRate(),
                                    bo.getPln(), bo.getInPriceEur(), bo.getInPriceUsd(), sl));
                            i += sl;
                            jc.setNum(jc.getNum() - sl);
                        } else {
                            res.add(profitCheckInitManager.genPyProfitCheckBO(jc, th.getDate(), bo.getCurrencyRate(),
                                    bo.getPln(), bo.getInPriceEur(), bo.getInPriceUsd(), jc.getNum()));
                            jc.setNum(0);
                            break;
                        }
                    }
                } else {
                    res.add(profitCheckInitManager.genPyProfitCheckBO(jc, date, bo.getCurrencyRate(), bo.getPln(),
                            bo.getInPriceEur(), bo.getInPriceUsd(), jc.getNum()));
                    jc.setNum(Math.abs(sum - jc.getNum()));
                }
            } else {
                //无进仓无转运
                res.add(profitCheckInitManager.genPyProfitCheckBO(jc, date, bo.getCurrencyRate(),
                        bo.getPln(), bo.getInPriceEur(), bo.getInPriceUsd(), jc.getNum()));
                jc.setNum(0);
            }
        }

        remainJcList = remainJcList.stream().filter(l -> l.getNum() != 0).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(remainJcList)) {
            log.error("剩余进仓列表数据如下：");
            remainJcList.forEach(x -> log.error(x.toString()));
            return;
        }

        //核对数据（库存、数量）
        List<TempKcjlDaily> kcRemoveList = new LinkedList<>();
        for (TempKcjlDaily daily : kcList) {
            List<ProfitCheckBO> list = res.stream().filter(l -> l.getSku().equals(daily.getCpbh()) &&
                    l.getPlace().equals(daily.getPlace()) &&
                    l.getBelongs().equals(daily.getBelongs())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(list)) {
                int num = list.stream().mapToInt(ProfitCheckBO::getRemainingNum).sum();
                if (num == daily.getSl()) {
                    kcRemoveList.add(daily);
                } else {
                    daily.setSl(daily.getSl() - num);
                }
            }
        }
        kcList.removeAll(kcRemoveList);

        if (!CollectionUtils.isEmpty(kcList)) {
            log.error("剩余未处理的数据如下：" + kcList);
            return;
        }

        List<String> noPriceSkuList = res.stream().filter(l -> Objects.isNull(l.getCurrencyRate()))
                .map(ProfitCheckBO::getSku).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(noPriceSkuList)) {
            List<SkuPriceInfoBO> skuPriceList = metaRelationMapper.getListBySkuList(noPriceSkuList);
            Map<String, List<SkuPriceInfoBO>> skuPriceMap = skuPriceList.stream().collect(Collectors.groupingBy(SkuPriceInfoBO::getSku));
            res.stream().filter(l -> Objects.isNull(l.getCurrencyRate())).forEach(
                    l -> {
                        List<SkuPriceInfoBO> list = skuPriceMap.get(l.getSku());
                        if (!CollectionUtils.isEmpty(list)) {
                            Optional<SkuPriceInfoBO> skuOprional = Optional.of(list.stream()
                                    .filter(d -> d.getBelongs().equals(l.getBelongs().toLowerCase()))
                                    .max(Comparator.comparing(SkuPriceInfoBO::getHtrq))).get();
                            if (skuOprional.isPresent()) {
                                l.setPln(skuOprional.get().getPln());
                                l.setCurrencyRate(skuOprional.get().getRate());
                                l.setCurrency(skuOprional.get().getCurrency());
                                if (l.getCurrency().equals("USD")) {
                                    l.setInPriceUsd(skuOprional.get().getPrice());
                                } else {
                                    l.setInPriceEur(skuOprional.get().getPrice());
                                }
                            } else {
                                String belongs;
                                String company = l.getBelongs().split("-")[0];
                                String country = l.getBelongs().split("-")[1];
                                if (company.equalsIgnoreCase("costway")) {
                                    belongs = "fds-" + country;
                                } else {
                                    belongs = "costway-" + country;
                                }
                                Optional<SkuPriceInfoBO> nextBo = list.stream().filter(d -> d.getBelongs().equals(belongs))
                                        .max(Comparator.comparing(SkuPriceInfoBO::getHtrq));
                                if (nextBo.isPresent()) {
                                    l.setPln(nextBo.get().getPln());
                                    l.setCurrencyRate(nextBo.get().getRate());
                                    l.setCurrency(nextBo.get().getCurrency());
                                    if (l.getCurrency().equals("USD")) {
                                        l.setInPriceUsd(nextBo.get().getPrice());
                                    } else {
                                        l.setInPriceEur(nextBo.get().getPrice());
                                    }
                                } else {
                                    SkuPriceInfoBO max = list.stream().max(Comparator.comparing(SkuPriceInfoBO::getHtrq)).get();
                                    l.setPrice(max.getPrice());
                                    l.setPln(max.getPln());
                                    l.setCurrencyRate(max.getRate());
                                    l.setCurrency(max.getCurrency());
                                    if (l.getCurrency().equals("USD")) {
                                        l.setInPriceUsd(max.getPrice());
                                    } else {
                                        l.setInPriceEur(max.getPrice());
                                    }
                                }
                            }
                        }
                    });
        }


        Map<String, ProfitCheckBO> resMap = res.stream().filter(l -> l.getRemainingNum() != 0)
                .collect(Collectors.groupingBy(l -> l.getSku() + l.getInvoiceNumber() + l.getBelongs(),
                        Collectors.collectingAndThen(Collectors.toList(), d -> {
                            int total = d.stream().mapToInt(ProfitCheckBO::getRemainingNum).sum();
                            ProfitCheckBO bo = d.get(0);
                            bo.setRemainingNum(total);
                            return bo;
                        })));
        LocalDateTime createTime = LocalDateTime.now();
        List<List<ProfitCheckBO>> spanList = DataMethod.splitData(resMap.values(), 2000);
        for (List<ProfitCheckBO> list : spanList) {
            List<ProfitCheck> profitCheckList = new LinkedList<>();
            List<ProfitCheckDaily> profitCheckDailyList = new LinkedList<>();
            List<ProfitCheckRecord> profitCheckRecordList = new LinkedList<>();
            Map<String, List<ProfitCheckBO>> profitCheckMap = list.stream()
                    .collect(Collectors.groupingBy(p -> p.getSku() + p.getPlace() +
                            (StringUtils.isNotBlank(p.getInvoiceNumber()) ? p.getInvoiceNumber() : "")));

            profitCheckMap.forEach((k, l) -> {
                int id = identifierGenerator.nextId(new ProfitCheck()).intValue();
                int remainingNum = l.stream().mapToInt(ProfitCheckBO::getRemainingNum).sum();
                ProfitCheck profitCheck = ProfitCheck.builder().id(id).sku(l.get(0).getSku()).type("init")
                        .invoiceNo(l.get(0).getInvoiceNumber()).belongs(l.get(0).getBelongs()).date(date)
                        .remainingNum(remainingNum).createTime(createTime).build();
                profitCheckList.add(profitCheck);
                profitCheckDailyList.add(ProfitCheckDaily.builder().profitCheckId(id).date(date).numFlag(true)
                        .beginStockNum(0).endStockNum(remainingNum).build());
                for (ProfitCheckBO bo : l) {
                    ProfitCheckRecord profitCheckRecord = ProfitCheckRecord.builder().profitCheckId(id)
                            .invoiceDate(bo.getInvoiceDate()).inboundDate(bo.getInboundDate()).type(bo.getType())
                            .endStockNum(bo.getRemainingNum()).pln(bo.getPln()).inPriceEur(bo.getInPriceEur())
                            .inPriceUsd(bo.getInPriceUsd()).currencyRate(bo.getCurrencyRate()).build();
                    profitCheckRecordList.add(profitCheckRecord);
                }
            });
            profitCheckRepository.batchSave(profitCheckList);
            profitCheckDailyRepository.batchSave(profitCheckDailyList);
            profitCheckRecordRepository.batchSave(profitCheckRecordList);
        }

    }


    @Override
    public List<ProfitInfoVO> exportProfitReport(String date, String belongs) {
        List<ProfitCheckBO> list = profitCheckMapper.getListByDateAndBelongs(date, belongs);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        return BeanCopyUtils.turnToVO(list, ProfitInfoVO.class);
    }

    @Override
    public void profitEvenUp(String date) {
        List<Pk> res = new ArrayList<>();
        List<ProfitCheckBO> costwayList = profitCheckMapper.getListByDateAndBelongs(date, null);
        Map<String, List<ProfitCheckBO>> costwayMap = costwayList.stream()
                .collect(Collectors.groupingBy(ProfitCheckBO::getBelongs));
        String fileDateName = LocalDate.parse(date).format(DateTimeFormatter.ofPattern("yyyyMM"));
        for (String company : COSTWAY_COMPANY_LIST) {
            System.out.println(company);
            String fileName = company + "_" + fileDateName;
            String path = FileUtil.getExcelPath() + PROFIT_CHECK_FOLDER_NAME + fileName + ".csv";
            List<ProfitCheckBO> list = EasyExcel.read(path).head(ProfitCheckBO.class).sheet().doReadSync();
            list = list.stream().filter(l -> l.getEndOfTheMonthStock() != 0).collect(Collectors.toList());
            List<ProfitCheckBO> kcCountryList = costwayMap.get(company.toUpperCase());
            List<Pk> costwayPkList = getResByContrastSku(company, date, list, kcCountryList);
            List<String> skuList = costwayPkList.stream().filter(l -> StringUtils.isBlank(l.getPlace()) && l.getNum() > 0)
                    .map(Pk::getSku).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(skuList)) {
                List<TempKcjlDaily> placeList = tempKcjlDailyMapper.getPlaceListBySkuListAndDate(skuList, date, company);
                Map<String, String> placeMap = placeList.stream()
                        .collect(Collectors.toMap(TempKcjlDaily::getCpbh, TempKcjlDaily::getPlace));
                costwayPkList.stream().filter(l -> StringUtils.isBlank(l.getPlace()) && l.getNum() > 0).forEach(l -> {
                    String place = placeMap.get(l.getSku());
                    if (StringUtils.isNotBlank(place)) {
                        l.setPlace(place);
                    }
                });
            }
            res.addAll(costwayPkList);
        }
        List<List<Pk>> splitData = DataMethod.splitData(res, 2000);
        for (List<Pk> pkList : splitData) {
            pkMapper.insertBatchSomeColumn(pkList);
        }
    }


    public List<Pk> getResByContrastSku(String belongs, String date, List<ProfitCheckBO> list, List<ProfitCheckBO> kcCountryList) {
        List<Pk> res = new LinkedList<>();
        Map<String, List<ProfitCheckBO>> listSkuMap = list.stream().collect(Collectors.groupingBy(ProfitCheckBO::getSku));
        Map<String, List<ProfitCheckBO>> kcSkuMap = kcCountryList.stream().collect(Collectors.groupingBy(ProfitCheckBO::getSku));
        Set<String> skuList = new HashSet<>(listSkuMap.keySet());
        skuList.addAll(kcSkuMap.keySet());
//        String tomorrow = DateExtUtil.getAfterNDays(1, date);
        LocalDateTime createTime = LocalDateTime.now();
        for (String sku : skuList) {
            List<ProfitCheckBO> profitSkuList = listSkuMap.get(sku);
            List<ProfitCheckBO> kcSkuList = kcSkuMap.get(sku);
            if (CollectionUtils.isEmpty(profitSkuList)) {
                //盘盈
                genPkList(res, kcSkuList, date, createTime);
                continue;
            }
            if (CollectionUtils.isEmpty(kcSkuList)) {
                //盘亏
                profitSkuList.forEach(l -> {
                    l.setEndOfTheMonthStock(Math.negateExact(l.getEndOfTheMonthStock()));
                    l.setBelongs(belongs);
                });
                genPkList(res, profitSkuList, date, createTime);
                continue;
            }
            int profitNum = profitSkuList.stream().mapToInt(ProfitCheckBO::getEndOfTheMonthStock).sum();
            int kcNum = kcSkuList.stream().mapToInt(ProfitCheckBO::getEndOfTheMonthStock).sum();
            if (profitNum > kcNum) {
                //盘亏
                int pkNum = profitNum - kcNum;
                ProfitCheckBO profit = ProfitCheckBO.builder().sku(sku).endOfTheMonthStock(Math.negateExact(pkNum))
                        .belongs(belongs).build();
                genPkList(res, Collections.singletonList(profit), date, createTime);
                continue;
            }
            if (profitNum < kcNum) {
                //盘盈(需要看下合同号码是否冲突，需要进行排除；如果全冲突，那就需要用上hy得表，看看是否有其他合同号，可以进行盘盈)
                int pyNum = kcNum - profitNum;
                List<String> profitInvoiceNoList = profitSkuList.stream().map(ProfitCheckBO::getInvoiceNumber)
                        .collect(Collectors.toList());
                List<ProfitCheckBO> kcFilterList = kcSkuList.stream()
                        .filter(l -> !profitInvoiceNoList.contains(l.getInvoiceNumber())).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(kcFilterList)) {
                    //其他合同号
                    pyNum = otherInvoiceNoGen(belongs, profitNum, pyNum, sku, date, profitInvoiceNoList, res, createTime);
                } else {
                    for (ProfitCheckBO kc : kcFilterList) {
                        if (pyNum == 0) {
                            break;
                        }
                        int num = kc.getEndOfTheMonthStock() - pyNum;
                        ProfitCheckBO profit = ProfitCheckBO.builder().sku(sku).inPriceEur(kc.getInPriceEur())
                                .inPriceUsd(kc.getInPriceUsd()).belongs(kc.getBelongs())
                                .invoiceNumber(kc.getInvoiceNumber()).build();
                        if (num >= 0) {
                            profit.setEndOfTheMonthStock(pyNum);
                            pyNum = 0;
                        } else {
                            profit.setEndOfTheMonthStock(kc.getEndOfTheMonthStock());
                            pyNum = Math.abs(num);
                        }
                        genPkList(res, Collections.singletonList(profit), date, createTime);
                    }
                    if (pyNum > 0) {
                        pyNum = otherInvoiceNoGen(belongs, profitNum, pyNum, sku, date, profitInvoiceNoList, res, createTime);
                    }
                }
                CommonException.throwExOnCondition(pyNum > 0, "FA_error_py", sku);
            }
        }
        return res;
    }

    private void genPkList(List<Pk> res, List<ProfitCheckBO> profit, String tomorrow, LocalDateTime createTime) {
        res.addAll(
                BeanCopyUtils.copyListProperties(profit, Pk::new, (s, t) -> {
                    t.setId(null);
                    t.setNum(s.getEndOfTheMonthStock());
                    if (Objects.nonNull(s.getInPriceEur())) {
                        t.setPrice(CurrencyConvertUtil.convertFenFromYuanStandard(s.getInPriceEur()));
                    } else {
                        t.setPrice(CurrencyConvertUtil.convertFenFromYuanStandard(s.getInPriceUsd()));
                    }
                    t.setType(PkTypeEnum.PROFIT.getValue());
                    t.setInvoiceNo(s.getInvoiceNumber());
                    t.setDate(tomorrow);
                    t.setCreateTime(createTime);
                })
        );
    }

    /**
     * 其他合同号处理
     *
     * @param belongs             货权
     * @param pyNum               盘盈数量
     * @param sku                 产品编号
     * @param date                日期
     * @param profitInvoiceNoList 发票列表
     * @param res                 结果集
     * @param createTime          创建时间
     */
    private int otherInvoiceNoGen(String belongs, int sl, int pyNum, String sku, String date, List<String> profitInvoiceNoList,
                                  List<Pk> res, LocalDateTime createTime) {
        List<SkuPriceBO> skuInfoList = tempKcjlDailyHyMapper.getListByCondition(sku, date);
        CommonException.throwExOnCondition(CollectionUtils.isEmpty(skuInfoList), "FA_error_py", sku);
        //包含了发票号，那么要减去对应的数量
        Map<String, List<SkuPriceBO>> invoiceNoMap = skuInfoList.stream()
                .collect(Collectors.groupingBy(SkuPriceBO::getInvoiceNo));
        for (String invoiceNo : profitInvoiceNoList) {
            if (sl == 0) {
                break;
            }
            List<SkuPriceBO> commonList = invoiceNoMap.get(invoiceNo);
            if (!CollectionUtils.isEmpty(commonList)) {
                for (SkuPriceBO bo : commonList) {
                    int num = bo.getSl() - sl;
                    if (num > 0) {
                        bo.setSl(num);
                        break;
                    } else {
                        bo.setSl(0);
                        sl = Math.abs(num);
                    }
                }
            }
        }
        skuInfoList = skuInfoList.stream().filter(l -> l.getSl() != 0).collect(Collectors.toList());
        for (SkuPriceBO info : skuInfoList) {
            if (pyNum == 0) {
                break;
            }
            int num = info.getSl() - pyNum;
            ProfitCheckBO profit = ProfitCheckBO.builder().sku(sku).belongs(belongs)
                    .place(info.getPlace()).invoiceNumber(info.getInvoiceNo()).endOfTheMonthStock(pyNum).build();
            if (info.getCurrency().equals("EUR")) {
                profit.setInPriceEur(info.getPrice());
            } else {
                profit.setInPriceUsd(info.getPrice());
            }

            if (num >= 0) {
                profit.setEndOfTheMonthStock(pyNum);
                pyNum = 0;
            } else {
                profit.setEndOfTheMonthStock(info.getSl());
                pyNum = Math.abs(num);
            }
            genPkList(res, Collections.singletonList(profit), date, createTime);
        }
        return pyNum;
    }

    @Override
    public void genDiffKc(String beforeDate, String afterDate) {
        List<DiffKc> res = new LinkedList<>();
        List<TempKcjlDaily> kcList = tempKcjlDailyMapper.getCostwayListInDate(beforeDate, afterDate);
        Map<String, List<TempKcjlDaily>> belongsKcMap = kcList.stream().collect(Collectors.groupingBy(TempKcjlDaily::getBelongs));
        String fileDateName = LocalDate.parse(beforeDate).format(DateTimeFormatter.ofPattern("yyyyMM"));
        for (String company : COSTWAY_COMPANY_LIST) {
            //需要时间为beforeDate的利润检查表中，期末库存来确定真正需要补充多少数量
            System.out.println(company);
            String fileName = company + "_" + fileDateName;
            String path = FileUtil.getExcelPath() + PROFIT_CHECK_FOLDER_NAME + fileName + ".csv";
            List<ProfitCheckBO> list = EasyExcel.read(path).head(ProfitCheckBO.class).sheet().doReadSync();
            genDiffKcList(beforeDate, afterDate, company, belongsKcMap.get(company.toUpperCase()), list, res);
        }
        List<List<DiffKc>> splitData = DataMethod.splitData(res, 2000);
        for (List<DiffKc> diffKc : splitData) {
            diffKcMapper.insertBatchSomeColumn(diffKc);
        }
    }

    private void genDiffKcList(String beforeDate, String afterDate, String belongs, List<TempKcjlDaily> belongsKcList,
                               List<ProfitCheckBO> list, List<DiffKc> res) {
        Map<String, List<ProfitCheckBO>> profitSkuMap = list.stream().filter(l -> l.getEndOfTheMonthStock() != 0)
                .collect(Collectors.groupingBy(ProfitCheckBO::getSku));
        Map<String, List<TempKcjlDaily>> kcSkuMap = belongsKcList.stream().collect(Collectors.groupingBy(TempKcjlDaily::getCpbh));
        Map<String, TempKcjlDaily> skuDateMap = belongsKcList.stream()
                .collect(Collectors.toMap(l -> l.getCpbh() + l.getDate(), i -> i));
        Set<String> skuList = new HashSet<>(profitSkuMap.keySet());
        skuList.addAll(kcSkuMap.keySet());
        for (String sku : skuList) {
            List<ProfitCheckBO> pList = profitSkuMap.get(sku);
            List<TempKcjlDaily> kList = kcSkuMap.get(sku);
            Integer beforeSl = Optional.ofNullable(skuDateMap.get(sku + beforeDate)).map(TempKcjlDaily::getSl).orElse(0);
            Integer afterSl = Optional.ofNullable(skuDateMap.get(sku + afterDate)).map(TempKcjlDaily::getSl).orElse(0);
            if (CollectionUtils.isEmpty(pList)) {
                res.add(genDiffKc(sku, belongs, beforeDate, afterDate, beforeSl - afterSl, 0));
                continue;
            }
            if (CollectionUtils.isEmpty(kList)) {
                continue;
            }
            int profitSum = pList.stream().mapToInt(ProfitCheckBO::getEndOfTheMonthStock).sum();
            if ((beforeSl - afterSl) > profitSum) {
                res.add(genDiffKc(sku, belongs, beforeDate, afterDate, beforeSl - afterSl, profitSum));
            }
        }
    }

    private DiffKc genDiffKc(String sku, String belongs, String beforeDate, String afterDate, int diffNum, int profitNum) {
        int needNum;
        if (diffNum > profitNum) {
            //不够扣减
            needNum = diffNum - profitNum;
        } else {
            needNum = 0;
        }
        return DiffKc.builder().sku(sku).belongs(belongs).beforeDate(LocalDate.parse(beforeDate))
                .afterDate(LocalDate.parse(afterDate)).diffNum(diffNum).profitEndNum(profitNum).needNum(needNum).build();
    }

}
